#include "network/sockaddr.hpp"

#include <iostream>
#include <sstream>

#include <arpa/inet.h>
#include <sys/un.h>

namespace ibox::network {

using namespace std;

const size_t SockAddrUnHeadSize
    = (size_t) & (((struct sockaddr_un *) nullptr)->sun_path);

SockAddr::SockAddr() { bzero(&m_addr, sizeof(m_addr)); }

SockAddr::SockAddr(const IpAddress &ip, uint16_t port)
    : m_len{sizeof(struct sockaddr_in)} {
    bzero(&m_addr, sizeof(m_addr));

    auto *p_addr = (struct sockaddr_in *) &m_addr;
    p_addr->sin_family = AF_INET;
    p_addr->sin_addr.s_addr = (in_addr_t) ip;
    p_addr->sin_port = htons(port);
}

SockAddr::SockAddr(const struct sockaddr &addr, socklen_t len) : m_len{len} {
    ::memcpy(&m_addr, &addr, len);
}

SockAddr::SockAddr(const struct sockaddr_in &addr) : m_len{sizeof(addr)} {
    ::memcpy(&m_addr, &addr, sizeof(addr));
}

SockAddr::SockAddr(const SockAddr &other) : m_len{other.m_len} {
    // 拷贝整个结构体, 或者用麻烦的方式, 成员变量逐一拷贝
    ::memcpy(&m_addr, &other.m_addr, m_len);
}

SockAddr &SockAddr::operator=(const SockAddr &other) {
    if (this != &other) {
        m_len = other.m_len;
        ::memcpy(&m_addr, &other.m_addr, m_len);
    }
    return *this;
}

SockAddr SockAddr::from_string(const string &addr_str) {
    //! 看看是不是 '192.168.23.44:9999' 格式的
    auto colon_pos = addr_str.find(':');
    // if (colon_pos != string::npos) {
    //     //! 当成 IPv4 处理
    //     auto ipv4_str = addr_str.substr(0, colon_pos);
    //     auto port_str = addr_str.substr(colon_pos + 1);

    //     try {
    //         uint16_t port = stoi(port_str);
    //         return SockAddr(IpAddress::from_string(ipv4_str), port);
    //     } catch (const std::exception &e) {
    //         return SockAddr();
    //     }
    // } else {
    //     //! 作为Local处理
    //     return SockAddr(DomainSockPath(addr_str));
    // }
    //! 当成 IPv4 处理
    auto ipv4_str = addr_str.substr(0, colon_pos);
    auto port_str = addr_str.substr(colon_pos + 1);

    try {
        uint16_t port = stoi(port_str);
        return SockAddr(IpAddress::from_string(ipv4_str), port);
    } catch (const std::exception &e) {
        return SockAddr();
    }
}

SockAddr::Type SockAddr::type() const {
    switch (m_addr.ss_family) {
        case AF_INET:
            return Type::IPv4;
        case AF_LOCAL:
            return Type::Local;
        default:
            return Type::None;
    }
}

namespace {
string to_ipv4_string(const struct sockaddr_storage &addr) {
    auto *p_addr = (struct sockaddr_in *) &addr;
    char ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &p_addr->sin_addr, ip, sizeof(ip));
    ostringstream oss;
    oss << ip << ':' << ntohs(p_addr->sin_port);
    return oss.str();
}

string to_local_string(const struct sockaddr_storage &addr, socklen_t len) {
    auto *p_addr = (struct sockaddr_un *) &addr;
    return string(
        p_addr->sun_path,
        len - SockAddrUnHeadSize); //! Path 未必是以 \0 结束的，要指定长度
}
} // namespace

string SockAddr::to_string() const {
    switch (m_addr.ss_family) {
        case AF_INET:
            return to_ipv4_string(m_addr);
        case AF_LOCAL:
            return to_local_string(m_addr, m_len);
    }
    std::cout << "unspport family\n";
    return "???";
}

bool SockAddr::get(IpAddress &ip, uint16_t &port) const {
    if (m_addr.ss_family == AF_INET) {
        auto *p_addr = (struct sockaddr_in *) &m_addr;
        ip = p_addr->sin_addr.s_addr;
        port = ntohs(p_addr->sin_port);
        return true;
    }
    std::cout << "type not match\n";
    return false;
}

bool SockAddr::operator==(const SockAddr &rhs) const {
    if (m_len != rhs.m_len) {
        return false;
    }

    return ::memcmp(&m_addr, &rhs.m_addr, m_len) == 0;
}
} // namespace ibox::network
